package simulation.listeners;

import java.util.HashMap;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.general.DatasetUtilities;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;

import layers.physical.Truck;
import events.SimulationStoppedEvent;
import events.truckEvents.TruckCreatedEvent;
import events.truckEvents.TruckEndedDrivingToDestinationEvent;
import events.truckEvents.TruckEndedDrivingToPackageEvent;
import events.truckEvents.TruckEndedSearchingPackageEvent;
import events.truckEvents.TruckStartedDrivingToDestinationEvent;
import events.truckEvents.TruckStartedDrivingToPackageEvent;
import events.truckEvents.TruckStartedSearchingPackageEvent;
import framework.core.VirtualClock;
import framework.events.Event;
import framework.events.EventBroker;
import framework.events.EventListener;

public class TruckEventListener extends ApplicationFrame implements EventListener{

	private static final long serialVersionUID = 2173182162536280232L;
	private HashMap<Truck, Double> searchingPackageTimeTotal, drivingToPackageTimeTotal, drivingToDestinationTimeTotal,
	startedSearchingPackageTime, startedDrivingToPackage, startedDrivingToDestination;
	private int amountOfTrucks = 0;

	public TruckEventListener(String title){
		super(title);
		initialize();
		EventBroker.getEventBroker().register(this);
	}

	private void initialize(){
		searchingPackageTimeTotal = new HashMap<Truck, Double>();
		drivingToPackageTimeTotal = new HashMap<Truck, Double>();
		drivingToDestinationTimeTotal = new HashMap<Truck, Double>();
		startedSearchingPackageTime = new HashMap<Truck, Double>();
		startedDrivingToPackage = new HashMap<Truck, Double>();
		startedDrivingToDestination = new HashMap<Truck, Double>();
	}

	@Override
	public void handleEvent(Event event) {
		if(event instanceof TruckCreatedEvent){
			TruckCreatedEvent e = (TruckCreatedEvent) event;
			this.handleTruckCreatedEvent(e);
		}else if(event instanceof TruckStartedSearchingPackageEvent){
			TruckStartedSearchingPackageEvent e = (TruckStartedSearchingPackageEvent) event;
			this.handleTruckStartedSearchingPackageEvent(e);
		}else if(event instanceof TruckStartedDrivingToPackageEvent){
			TruckStartedDrivingToPackageEvent e = (TruckStartedDrivingToPackageEvent) event;
			this.handleTruckStartedDrivingToPackageEvent(e);
		}else if(event instanceof TruckStartedDrivingToDestinationEvent){
			TruckStartedDrivingToDestinationEvent e = (TruckStartedDrivingToDestinationEvent) event;
			this.handleTruckStartedDrivingToDestinationEvent(e);
		}else if(event instanceof TruckEndedSearchingPackageEvent){
			TruckEndedSearchingPackageEvent e = (TruckEndedSearchingPackageEvent) event;
			this.handleTruckEndedSearchingPackageEvent(e);
		}else if(event instanceof TruckEndedDrivingToDestinationEvent){
			TruckEndedDrivingToDestinationEvent e = (TruckEndedDrivingToDestinationEvent) event;
			this.handleTruckEndedDrivingToDestinationEvent(e);
		}else if(event instanceof TruckEndedDrivingToPackageEvent){
			TruckEndedDrivingToPackageEvent e = (TruckEndedDrivingToPackageEvent) event;
			this.handleTruckEndedDrivingToPackageEvent(e);
		}else if(event instanceof SimulationStoppedEvent){
			this.handleSimulationStoppedEvent();
		}
	}

	private void handleSimulationStoppedEvent() {
		final CategoryDataset dataset = createDataset();
		final JFreeChart chart = createChart(dataset);
		final ChartPanel chartPanel = new ChartPanel(chart);
		chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
		setContentPane(chartPanel);
		this.pack();
		RefineryUtilities.centerFrameOnScreen(this);
		this.setVisible(true);
	}

	private CategoryDataset createDataset() {
		double[][] data = new double[3][this.amountOfTrucks];
		int i = 0;
		String[] truckNames = new String[this.amountOfTrucks];
		for(Truck truck : this.searchingPackageTimeTotal.keySet()){
			double total = this.searchingPackageTimeTotal.get(truck) + this.drivingToPackageTimeTotal.get(truck) + this.drivingToDestinationTimeTotal.get(truck);
			data[0][i] = this.searchingPackageTimeTotal.get(truck) / total;
			data[1][i] = this.drivingToPackageTimeTotal.get(truck) / total;
			data[2][i] = this.drivingToDestinationTimeTotal.get(truck) / total;
			truckNames[i] = "Truck " + i;
			i++;
		}
		return DatasetUtilities.createCategoryDataset(new String[]{"Searching", "To package", "To destination"}, truckNames, data);
	}

	private JFreeChart createChart(final CategoryDataset dataset) {  	
		final JFreeChart chart = ChartFactory.createStackedBarChart(
				"Time spent by trucks",  // chart title
				"Trucks",                  // domain axis label
				"Time (in%)",                     // range axis label
				dataset,                     // data
				PlotOrientation.VERTICAL,    // the plot orientation
				true,                        // legend
				true,                        // tooltips
				false                        // urls
		);
		return chart;
	}

	private void handleTruckEndedDrivingToPackageEvent(
			TruckEndedDrivingToPackageEvent e) {
		Double time = VirtualClock.currentTime() - this.startedDrivingToPackage.get(e.getTruck());
		time += this.drivingToPackageTimeTotal.get(e.getTruck());
		this.drivingToPackageTimeTotal.put(e.getTruck(), time);
	}

	private void handleTruckEndedDrivingToDestinationEvent(
			TruckEndedDrivingToDestinationEvent e) {
		Double time = VirtualClock.currentTime() - this.startedDrivingToDestination.get(e.getTruck());
		time += this.drivingToDestinationTimeTotal.get(e.getTruck());
		this.drivingToDestinationTimeTotal.put(e.getTruck(), time);
	}

	private void handleTruckEndedSearchingPackageEvent(
			TruckEndedSearchingPackageEvent e) {
		Double time = VirtualClock.currentTime() - this.startedSearchingPackageTime.get(e.getTruck());
		time += this.searchingPackageTimeTotal.get(e.getTruck());
		this.searchingPackageTimeTotal.put(e.getTruck(), time);
	}

	private void handleTruckStartedDrivingToDestinationEvent(
			TruckStartedDrivingToDestinationEvent e) {
		Long time = VirtualClock.currentTime();
		this.startedDrivingToDestination.put(e.getTruck(), time.doubleValue());
	}

	private void handleTruckStartedDrivingToPackageEvent(
			TruckStartedDrivingToPackageEvent e) {
		Long time = VirtualClock.currentTime();
		this.startedDrivingToPackage.put(e.getTruck(), time.doubleValue());
	}

	private void handleTruckStartedSearchingPackageEvent(
			TruckStartedSearchingPackageEvent e) {
		Long time = VirtualClock.currentTime();
		this.startedSearchingPackageTime.put(e.getTruck(), time.doubleValue());
	}

	private void handleTruckCreatedEvent(TruckCreatedEvent e) {
		this.drivingToDestinationTimeTotal.put(e.getTruck(), 0d);
		this.drivingToPackageTimeTotal.put(e.getTruck(), 0d);
		this.searchingPackageTimeTotal.put(e.getTruck(), 0d);
		this.amountOfTrucks++;
	}

	@Override
	public void terminate() {
		//NO-OP
	}
}
